<?php
	
	namespace Aspic;
	
	class ClassLoader {
		
		protected static $_exts = array('.php', '.class.php', '.interface.php');

		protected static $_namespaces = array();
		protected static $_loaderIsRegistered = false;
		protected static $_namespaceToPathFunc = null;
		
		/**
		* @param mixed $nsToFileFunc A function that will transform the class namespace path to the correct file
		*/
		public static function registerLoader() {
			if(!static::$_loaderIsRegistered) {
				spl_autoload_register('static::loadClass');
				
				static::$_loaderIsRegistered = true;
				
				return true;
			}
			else {
				return false;
			}
		}
		
		public static function loadClass($classPath) {
			$path = static::getNamespacePath($classPath);
			
			if(is_file($path)) {
				require $path;
				
				if (!class_exists($classPath, false) AND !interface_exists($classPath, false) AND !trait_exists($classPath, false)) {
					throw new Exception('Class / Interface / Trait "'.$classPath.'" not found in file "'.$path.'"');
				}
				else {
					return true;
				}

			}
			
			return false;
		}
		
		/**
		* Return the path to a given namespace or class
		*/
		public static function getNamespacePath($namespace) {
			// ex: a\b\c
			$firstNs = substr($namespace, 0, strpos($namespace, '\\')); // = a
			$nextNsPath = substr($namespace, strpos($namespace, '\\') + 1); // b\c
			
			if (!isset(static::$_namespaces[$firstNs])) {
				return null; // Maybe just testing if a class exists without any namespace behind like class_exists('classA')
			}

			$nsPath = static::$_namespaces[$firstNs];
			
			$parsedPath = static::namespaceToPath($nsPath, $nextNsPath);
			
			return $parsedPath;
		}
		
		/**
		 *
		 * @param type $nsPath The path to the namespace directory previously found in static::$_namespaces
		 * @param string $classPath The following namespace path to convert
		 * @return string 
		 */
		public static function namespaceToPath($nsPath, $classPath) {
			if(!is_null(static::$_namespaceToPathFunc)) {
				return call_user_func(static::$_namespaceToPathFunc, $nsPath, $classPath);
			}
			else {
				$classPath = $nsPath.DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $classPath);
				
				foreach(static::$_exts as $suffix) {
					$fullPath = $classPath.$suffix;

					if(is_file($fullPath)) {
						return $fullPath;
					}
				}

				return $classPath; // Return path to namespace and not class
			}
		}
		
		public static function setNamespaceToPathFunc($function) {
			static::$_namespaceToPathFunc = $function;
		}
		
		/**
		 *
		 * @param type $namespace A namespace like "Lib"
		 * @param type $path Path to namespace like "c:\www\Lib"
		 */
		public static function registerNamespacePath($namespace, $path) {
			$path = trim($path, '\\');
			
			static::$_namespaces[$namespace] = $path;
			
			static::registerLoader();
		}

		
	}	
	